import hashlib, binascii,time,uuid,json
import struct,random,string
import socket
from contextlib import closing
import platform,time,sys

con_isDebug = False
con_listenIp = '0.0.0.0'
con_localPort = 9997
recLen = 10240
con_recLen = 1024*200
bufferSize = 30000
con_streamBufferSize = 200*1024
eachConnWriteLimit = 200*1024
tcp_maxBuffSize =  200*1024
servicePort = 19022
serviceSaltKey = 'bigSalt'
packetsStatisTime = 1


#the following parameters can be changed on the client alone, you don't need to change them on the server.
timeoutTime = 1
con_serverIp = '154.221.19.38'
#con_serverIp = '127.0.0.1'
#con_serverIp = '192.168.43.173'
#con_serverIp = '202.182.122.187'
#con_serverIp =  '192.168.1.105'
#con_serverIp =  '155.138.216.45'
maxPortNum = 100
minPortNum = 10
con_portIncreaseDose = 3
con_portDecreaseDose = 1
minPackGot = 100
con_minRate = 0.08
con_maxRate = 0.5
con_pushAhead = 2000
con_packLimit = 1400
regulateTimeSpan = 1
perSecondPortsLimit = 50
con_closeTime = 50


def getCustomPortsLimit(currentPortNum,gotPacketsNum,lostPacketsNum,rawReadSpeed,rawWriteSpeed):
    if rawReadSpeed>250*1024:
        if  lostPacketsNum>10:
            return perSecondPortsLimit,-1
        else:
            return perSecondPortsLimit,0
    return perSecondPortsLimit,con_portIncreaseDose


maxSendConfig=[{'small':0,'big':0.05,'slope':1,'maxSend':3},
               {'small':0.05,'big':0.1,'slope':1.5,'maxSend':3},
               {'small':0.1,'big':0.4,'slope':2,'maxSend':4},
               {'small':0.4,'big':0.6,'slope':3,'maxSend':5},
               {'small':0.6,'big':0.8,'slope':4,'maxSend':8},
               {'small':0.8,'big':1,'slope':5,'maxSend':10},]



  



platformName = platform.system()
pyV = sys.version_info[0]
def getRunningTime():    
    if pyV == 3:
        return time.monotonic()
    if platformName=='Windows':
        return time.clock()
    elif platformName=='Linux':
        with open('/proc/uptime') as f:
            return float(f.read().split()[0])
    else:
        return time.time()
def getPackStaBigV(m):
    mv = 0
    for k,v in m.items():
        if getRunningTime()-v['time']<packetsStatisTime and v['v']>mv:
            mv = v['v']
    return mv

def clearPackSta(m):
    k = list(m.keys())
    for i in k:
        if m[i]['time']<getRunningTime()-packetsStatisTime:
            del m[i]
            
def addPackSta(m,v,ti=0):
    if ti==0 or ti==float('inf'):
        ti = getRunningTime()
    m[str(uuid.uuid1())] = {'time':ti,'v':v}
    
class structWrapper():
    def __init__(self,s=b''):
        self.data = s
        self.pos = 0
    def writeArr(self,a):
        self.data += a
        self.pos += len(a)
    def readArr(self,l):       
        self.pos += l   
        return self.data[self.pos-l:self.pos]
    def writeByte(self,b):
        self.data += struct.pack('b',b)
        self.pos += 1
    def writeWord(self,b):
        self.data += struct.pack('h',b)
        self.pos += 2    
    def writeDouble(self,b):
        self.data += struct.pack('d',b)
        self.pos += 8
    def readByte(self):
        r = struct.unpack('b',self.data[self.pos:self.pos+1])[0]
        self.pos += 1
        return r
    def readWord(self):
        r = struct.unpack('h',self.data[self.pos:self.pos+2])[0]
        self.pos += 2
        return r
    def readDouble(self):
        r = struct.unpack('d',self.data[self.pos:self.pos+8])[0]
        self.pos += 8
        return r    
    def getLeftData(self):
        return self.data[self.pos:]
    
def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return int(s.getsockname()[1])

def findNPorts(n):
    l = []
    while len(l)!=n:
        i = find_free_port()
        if i not in l:
            l.append(i)
    return l

def randomStringDigits(stringLength=6):
    lettersAndDigits = string.ascii_letters + string.digits
    return ''.join(random.choice(lettersAndDigits) for i in range(stringLength))

class TOUMsg():
    def __init__(self,m = {},s=b''):
        self.m_json = m
        self.strContetn = s
        self.length = 0
        
    def pack(self):
        j = json.dumps(self.m_json)
        j = j.encode()
        jL = len(j)
        cL = len(self.strContetn)
        self.length = 16+jL+cL
        return struct.pack('q',jL)+j+struct.pack('q',cL)+self.strContetn
    
    def unpack(self,s):
        if len(s)<16:
            return False,s
        jL = struct.unpack('q',s[:8])[0]
        if len(s)<16+jL:
            return False,s
        cL = struct.unpack('q',s[8+jL:16+jL])[0]
        if len(s)<16+jL+cL:
            return False,s
        self.m_json = json.loads(s[8:8+jL])
        self.strContetn = s[16+jL:16+jL+cL]
        self.length = 16+jL+cL
        return True,s[16+jL+cL:]
    
g_packId = 0

def makePack(s,salt):
    global g_packId
    g_packId += 1
    if g_packId>30000:
        g_packId = 0
    u2 = struct.pack('H',g_packId)  
    s1 = u2+s
    dk = hashlib.md5(s1+salt).digest()[:4]
    s2 = s1+dk
    return u2,s2

def checkPackValid(s,u,salt):
    if len(s)<4:
        return b''
    s1 = s[-4:]
    s2 = s[:-4]
    dk = hashlib.md5(s2+salt).digest()[:4]
    if dk != s1:
        return b''
    if s2[:2] != u:
        return b''
    return s2[2:]

def checkPackValid2(s,salt):
    if len(s)<4:
        return '',b''
    s1 = s[-4:]
    s2 = s[:-4]
    dk = hashlib.md5(s2+salt).digest()[:4]
    if dk != s1:
        return '',b''
    u = s2[:2]
    con = s2[2:]
    return u,con
    
    
def makePack_server(s,u,salt):
    s1 = u+s
    dk = hashlib.md5(s1+salt).digest()[:4]
    s2 = s1+dk
    return s2

def checkPackValid_server(s,salt):
    if len(s)<4:
        return '',b''
    s1 = s[-4:]
    s2 = s[:-4]
    dk = hashlib.md5(s2+salt).digest()[:4]   
    if dk != s1:
        return '',b''
    if len(s2)<2:
        return '',b''
    return s2[:2] ,s2[2:]

def circleBig(a,b,bs=bufferSize):
    if a==b:
        return a
    if a>b and (a-b)<(bs/2):
        return a
    if a<b and (b-a)>(bs/2):
        return a
    return b

def circleRange(a,b,bs=bufferSize):  # return [,)  same as range
    temp = a
    ret = []
    while True:
        if temp== circleBig(b,temp,bs):
            break
        ret.append(temp)
        temp = circleAdd(temp,1,bs)
    return ret

def circleMax(l,bs=bufferSize):
    ret = None
    k = l.keys()
    for i in k:
        if ret==None:
            ret = i
        ret = circleBig(i,ret,bs)
    return ret


def circleAddOne(a,bs=bufferSize):
    if a == bs-1:
        return 0
    return a+1

def circleAdd(a,b,bs=bufferSize):
    ret = a
    for i in range(b):
        ret = circleAddOne(ret,bs)
    return ret


if __name__ == '__main__':
    print (circleAddOne(8,10))
    print (circleAddOne(9,10))
    print (circleRange(98,2,100))
    u,s = makePack(b'sdfew',b'salt')
    print (u,s)
    s2 = checkPackValid(s, u, b'salt')
    print (s2)

